using System;
using System.Reflection;

namespace UnitTesting.Fixturing
{
	class MoqActivator : MockActivatorBase
	{
		const string TARGET_TYPE_NAME = "Moq.Mock`1";
		readonly Type _mockType;
		object _mock;
		readonly MockCreator _mockCreator;

		internal MoqActivator(Assembly assembly)
		{
			_mockType = ActivatorManager.GetTargetType(assembly, TARGET_TYPE_NAME);
			_mockCreator = new MockCreator(_mockType);
		}

		protected override object CreateInstance(Type type, object[] arguments)
		{
			CreateMock(type, arguments);
			return CreateMockObject();
		}

		void CreateMock(Type type, object[] arguments)
		{
			_mock = _mockCreator.CreateMock(type, arguments);
		}

		static BindingFlags GetBindingFlags()
		{
			return BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
		}

		object CreateMockObject()
		{
			var property = _mock.GetType().GetProperty("Object", GetBindingFlags());
			return property.GetValue(_mock, null);
		}

		class MockCreator
		{
			readonly Type _mockType;

			internal MockCreator(Type mockType)
			{
				_mockType = mockType;
			}

			internal object CreateMock(Type type, object[] arguments)
			{
				return CreateMockConstructor(type).Invoke(new object[] {arguments});
			}

			ConstructorInfo CreateMockConstructor(Type type)
			{
				var mockGenericType = _mockType.MakeGenericType(type);
				return mockGenericType.GetConstructor(new[] {typeof (object[])});
			}
		}
	}
}